-- 协同
-----------------------------------------------------------------------------------------
-- 状态
print("########")
co = coroutine.create(function(param)
    -- 2、运行过程中处于running
    print("create:status:" .. coroutine.status(co))
    for i = 1, 2 do
        print(param .. ":" .. i)
        -- 3、yield后处于suspended
        if i ~= 2 then
            coroutine.yield()
        end
    end
end)
-- 1、新建协同后处于suspended
print("create:status:" .. coroutine.status(co))
coroutine.resume(co, "create")
print("create:status:" .. coroutine.status(co))
coroutine.resume(co, "create")
print("create:status:" .. coroutine.status(co))
-----------------------------------------------------------------------------------------
-- wrap和crate
print("########")
wco = coroutine.wrap(function(param)
    for i = 1, 2 do
        print(param .. ":" .. i)
        if i ~= 2 then
            coroutine.yield()
        end
    end
end)
-- 1、调用方式不同，直接调用即可
wco("wrap")
-- 2、不能通过以下方式获取状态
-- print("wrap:statu:" .. coroutine.status(wrap))
-- 3、返回类型不同thread,function
print("co:" .. type(co))
print("wco:" .. type(wco))
-----------------------------------------------------------------------------------------
-- create参数和返回值
print("########")
pco = coroutine.create(function(step_1_param)
    print("step1:" .. step_1_param)
    local step_2_param_1, step_2_param_2 = coroutine.yield("step_1_return")
    print("step2:" .. "p1:" .. step_2_param_1)
    print("step2:" .. "p2:" .. step_2_param_2)
    return "step_2_return"
end)
-- 1、resume返回的第一个参数是coroutine是否被正确的执行
-- ?: 通过status获取协同的状态 or 多执行一次获取是否正确执行 来判断协同是否已经执行完成
print(coroutine.resume(pco, "p1"))
print(coroutine.resume(pco, "p1", "p2"))
print(coroutine.resume(pco))
-----------------------------------------------------------------------------------------
-- wrap参数以及返回值
print("########")
wpco = coroutine.wrap(function (step_1_param)
    print("step1:" .. step_1_param)
    local step_2_param_1, step_2_param_2 = coroutine.yield("step_1_return")
    print("step2:" .. "p1:" .. step_2_param_1)
    print("step2:" .. "p2:" .. step_2_param_2)
    return "step_2_return"
end)
print(wpco("p1"))
print(wpco("p1", "p2"))
-- 1、直接返回返回值没有是否正确执行的状态
-- 2、多运行一次会出错
-- ?: 如何判断协同已经被完全执行了
-- print(wpco())
-----------------------------------------------------------------------------------------
-- 生产者消费者
print("########")
productor = coroutine.create(function()
    local id = 0
    while true do
        id = id + 1
        print("create product " .. id)
        coroutine.yield(id)
    end
end)
consumer = function()
    for _ = 1, 10 do
        local _, product = coroutine.resume(productor)
        print("consum product " .. product)
    end
end
consumer()
-----------------------------------------------------------------------------------------
-- 回合制游戏
print("########")
caculator = coroutine.create(function()
    local player = {hp = 10, attack = 3}
    local enemy = {hp = 10, attack = 5}
    while true do
        enemy.hp = enemy.hp - player.attack
        if enemy.hp <= 0 then
            return "player", "enemy", "attack", enemy
        else
            coroutine.yield("player", "enemy", "attack", enemy)
        end
        player.hp = player.hp - enemy.attack
        if player.hp <= 0 then
            return "enemy", "player", "attack", player
        else
            coroutine.yield("enemy", "player", "attack", player)
        end
    end
end)
play = function()
    local round = 1
    repeat
        local result, from, to, attack_type, state = coroutine.resume(caculator)
        if result then
            print(from .. " " .. attack_type .. " " .. to)
            print("\t" .. to .. " left hp: " .. state.hp)
        end
        round = round + 1
    until not result or round == 10
end
play()
-----------------------------------------------------------------------------------------
print("########")
-- 纠正错误观点
-- 错误观点如下:
-- 1、
--  coroutine
--      delay(0.1, yield("haha"))
-- 2、
--  coroutine
--      delay(2, yield("xixi"))
-- 1和2是同时执行的coroutine，1在延迟0.1s后yield，2在延迟2s后yield
-- 因为coroutine.yield是不指定发给那个协同的，所以"haha"会被2接收到，"xixi"会被1接收到，打乱原本代码的设计

-- 代码验证
local eventQueue = {}
function laterCallback(delayTime, callback)
    local eventData = {}
    eventData.registerTime = os.clock()
    eventData.delayTime = delayTime
    eventData.callback = callback
    eventQueue[#eventQueue + 1] = eventData
end

function updateEvent()
    repeat
        for index, data in ipairs(eventQueue) do
            local current = os.clock()
            -- print(current, data.registerTime, data.delayTime)
            if current - data.registerTime >= data.delayTime then
                data.callback()
                table.remove(eventQueue, index)
                goto continue
            end
        end
        ::continue::
    until #eventQueue == 0
end

local conCallback = coroutine.wrap(function()
    laterCallback(0.1, function() coroutine.yield("haha") end)
end)
local conCallback2 = coroutine.wrap(function()
    laterCallback(1, function() coroutine.yield("haha") end)
end)

-- conCallback()
-- conCallback2()

updateEvent()

-- 执行结果
-- ERROR: attempt to yield from outside a coroutine

-- 错误分析
-- 因为conCallback没有及时的yield因而当调用后，已经处于dead的状态
-- 所以coroutine.yield("haha")的使用是错误的


